package DBIx::EntityRecord::Mapping;
use strict;
use Carp;
use XML::Simple qw/:strict/;
use Data::Dumper;

=over 4

=item CLASS->new( \%mapping_datastruct )

=item CLASS->new( -file => "/path/to/mapping/file" )

returns instance based on a configuration file or a hashref mapping data structure

=cut
sub new {
    my $class = shift;
    my $self;
    if (ref($_[0])) {
        $self = $class->new_from_ref(@_);
    }
    else {
        # expect external file
        $self = $class->new_from_file(@_);
    }
    
}


# helper routine: new_from_file( -file => "/path/to/mapping/file" )
sub new_from_file {
    my $class = shift;
    my $self = {};
    my %i = @_;
    (defined $i{'-file'})
        or croak "expected '-file' parameter for mapping filename";
    my $ref = XMLin( 
        $i{'-file'}, 
        ForceArray => 1, 
        KeyAttr => {
            entity      => "class",
            attribute   => "accessor",
            oid         => "attribute",
        },
    );
    return $class->new_from_ref( $ref );
}


# helper routine: new_from_file( { } )
sub new_from_ref {
    my $class = shift;
    my $self = {};
    bless $self, $class;
    my $ref = shift;
    if (exists $ref->{entity}) {
        # validate mapping
        foreach my $entity (keys %{$ref->{entity}}) {
            # validate existence of class
            #   and that the class is a subclass of DBIx::EntityRecord::BaseClass
            {
                no strict 'refs';
                (defined %{$entity."::"})
                    or croak "class $entity is undefined";
                my $isa = *{$entity."::ISA"};
                my $allok = 0;
                foreach my $sup (@$isa) {
                    if ($sup eq 'DBIx::EntityRecord::BaseClass') { $allok = 1; }
                }
                ($allok)
                    or croak "class $entity expected to be a DBIx::EntityRecord::BaseClass";
            }
            # validate existence of table
            ((defined $ref->{entity}->{$entity}->{table})
                and (length $ref->{entity}->{$entity}->{table} > 0))
                or croak "class $entity expected to map to a 'table'";
            # validate existence of field=>methods+column
            ((defined $ref->{entity}->{$entity}->{attribute})
                and (ref($ref->{entity}->{$entity}->{attribute}) eq 'HASH'))
                or croak "class $entity expected to have attribute mapping";
            foreach my $attrib (keys %{$ref->{entity}->{$entity}->{attribute}}) {
                # inspect column
                my $attribvals = $ref->{entity}->{$entity}->{attribute};
                (defined $attribvals->{$attrib}->{column})
                    or croak "class $entity attribute $attrib expected to map to a 'column'";
            }
            # validate existence of oid=>method undef fields
            foreach my $oidkey (keys %{$ref->{entity}->{$entity}->{oid}}) {
                # inspect accessors 
                my $oidvals = $ref->{entity}->{$entity}->{oid};
                my $attribvals = $ref->{entity}->{$entity}->{attribute};
                ((defined $oidkey)
                    # assume attributes have been checked
                    and (defined $attribvals->{$oidkey}))
                    or croak "class $entity oid $oidkey expected as attribute as well";
            }
        }
        $self->{conf} = $ref;
    }
    return $self;
}



=item $obj->table( $entity_class_name )

=cut
sub table {
    my $self = shift;
    my $entity = shift;
    if (($entity) and (exists $self->{conf}->{entity}->{$entity})) {
        return $self->{conf}->{entity}->{$entity}->{table};
    }
    return undef; 
}


=item $obj->attributes( $entity_class_name )

=cut
sub attributes {
    my $self = shift;
    my $entity = shift;
    if (($entity) and (exists $self->{conf}->{entity}->{$entity})) {
        return $self->{conf}->{entity}->{$entity}->{attribute};
    }
    return undef;
}


1;
__END__

=back

=head1 AUTHOR

Dexter Tad-y <dexterbt1 AT yahoo.com>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008 by Dexter Tad-y

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE SOFTWARE.

